using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Phoenix.Exceptions;
using Phoenix.Utilities;

namespace Phoenix.Framework
{
    public class ActionResolver
    {
        public MethodDescriptor ResolveFor(ControllerActionContext request, Type controllerType)
        {
            var possibleActions = controllerType
                .GetMethods()
                .Where(m => NameMatches(request, m))
                .Select(m => new MethodDescriptor(m))
                .ToArray();

            if (!possibleActions.Any())
                throw new ActionNotFoundException(request.ControllerAction.Action);

            return GetBestMatch(possibleActions, request);
        }

        private static MethodDescriptor GetBestMatch(IList<MethodDescriptor> possibleActions, ControllerActionContext request)
        {
            if (possibleActions.Count == 1)
            {
                if (request.Parameters.Count() == possibleActions[0].Parameters.Length)
                    return possibleActions[0];
            }

            var requestParameters = request.Parameters.ToArray();

            foreach (var possibleAction in possibleActions.OrderByDescending(p => p.Parameters.Length))
            {
                if (possibleAction.Parameters.Length > requestParameters.Length)
                    continue;

                var navigationParameters = request.Parameters.ToArray();

                var methodArguments = new List<ControllerActionParameter>();
                for (var index = 0; index < possibleAction.Parameters.Length; index++)
                {
                    var methodParameter = possibleAction.Parameters[index];
                    var navigationParameter = navigationParameters[index];
                    if (!navigationParameter.IsMatch(methodParameter.ParameterType))
                        break;

                    methodArguments.Add(navigationParameter);
                }

                //We didn't match all the method parameters
                if (methodArguments.Count != possibleAction.Parameters.Length)
                    continue;

                return possibleAction;
            }

            throw new ActionNotFoundException(request.ControllerAction.Action);
        }

        private static bool NameMatches(ControllerActionContext navigationRequest, MemberInfo m)
        {
            var name = m.Name;
            if (name.EndsWith("Async"))
                name = name.Substring(0, name.Length - 5);

            var actionName = navigationRequest.ControllerAction.Action;
            if (actionName.EndsWith("Async"))
                actionName = actionName.Substring(0, actionName.Length - 5);
            return SuffixHelper.RemoveViewSuffixes(name) == SuffixHelper.RemoveViewSuffixes(actionName);
        }
    }
}